const _ = require('lodash')
const RecommendUserService = class RecommendUserService {
constructor(data, userId, n) {
this.data = data
this.userId = userId
this.n = n
this.top = undefined
this.bottom = undefined
this.userArray = []
this.userGoodsTemp = []
this.similarityList = []
this.targetGoods = []
this.interestedGrade = [];
this.result = []
}


start() {

this.getUserArray()
this.similarityList.sort((a, b) => {
return b.grade - a.grade
})

this.getTargetGoods()

this.targetGoods = [...new Set(this.targetGoods)]

for (let goodsId of this.targetGoods.values()) {
this.getInterestedGrade(goodsId)
}

this.getFinalResult()
return this.result
}

getFinalResult() {
this.interestedGrade.sort((a, b) => {
return b.grade - a.grade
})
for (let obj of this.interestedGrade.values()) {
this.result.push(obj.goodsId)
}
}

getInterestedGrade(goodsId) {

let array = new Set()
for (let obj of this.data.values()) {
if (obj.goodsId === goodsId) {
array.add(obj.userId)
}
}
const users = [...array]

let grade = 0
for (let userId of users.values()) {
for (let i = 0; i < this.n; i++) {
if (userId === this.similarityList[i].userId) {
const res = this.getUserSimilarity(userId, this.similarityList[i].userId)
grade += res
}
}
}

this.interestedGrade.push({
goodsId: goodsId,
grade: grade
})
}

getTargetGoods() {

for (let index = 0; index < this.n; index++) {
const element = this.similarityList[index]
_.filter(this.data, obj => {
if (obj.userId == element.userId) {
this.targetGoods.push(obj.goodsId)
}
return obj.userId == element.userId
})
}

this.duplicateRemovalGoods()

}


duplicateRemovalGoods() {

const userGoods = _.filter(this.data, obj => {
return obj.userId == this.userId
})

for (let obj of userGoods.values()) {
if (this.targetGoods.includes(obj.goodsId)) {
this.targetGoods.splice(this.targetGoods.indexOf(obj.goodsId), 1)
}
}
}

getUserArray() {
const data = _.filter(this.data, obj => {
return obj.userId !== this.userId
})

let arrayTemp = []
for (let index in data) {
arrayTemp.push(data[index].userId)
}
this.userArray = [...(new Set(arrayTemp))]

this.n > this.userArray.length ? this.n = this.userArray.length : this.n = this.n

for (let index in this.userArray) {
this.getUserSimilarity(this.userId, this.userArray[index])
}
}

getUserSimilarity(userId, otherUserId) {
const userSelfGoods = _.filter(this.data, obj => {
return userId == obj.userId
})
this.filterUserGoods(otherUserId)

this.bottom = Math.sqrt(userSelfGoods.length * this.userGoodsTemp.length)

let count = 0
userSelfGoods.forEach(ele => {
for (let index in this.userGoodsTemp) {
if (ele.goodsId == this.userGoodsTemp[index].goodsId) {

const log = this.filterGoodsById(ele.goodsId)

count += log
}
}
})
this.top = count
const obj = {
userId: otherUserId,
grade: this.top / this.bottom
}
this.similarityList.push(obj)
return obj.grade
}


filterUserGoods(otherUserId) {
this.userGoodsTemp = _.filter(this.data, obj => {
return obj.userId == otherUserId
})
}

filterGoodsById(goodsId) {
const goods = _.filter(this.data, obj => {
return obj.goodsId == goodsId
})
return 1 / Math.log(1 + goods.length)
}
}

const RecommendGoodsService = class RecommendGoodsService {

constructor(data, userId, k, goodsId) {
this.data = data
this.goodsId = goodsId
this.userId = userId

this.k = k

this.goodsList = []

this.users = []

this.simpleList = []

this.userPerferList = []

this.goodsMayPerferList = []

this.resultRank = []

this.result = []
}


start() {

this.getInitialData()

for (let goodsId of this.goodsMayPerferList.values()) {
const res = this.getUserInterest(goodsId)
this.resultRank.push(res)
}

this.resultRank.sort((a, b) => {
return b.grade - a.grade
})

this.result = this.resultRank.reduce((array, obj) => {
array.push(obj.goodsId)
return array
}, [])
return this.result
}

getUserInterest(goodsId) {

const simple = this.getGoodsGrade(false, goodsId)
let grade = 0
for (let [index, obj] of simple.entries()) {
if (this.userPerferList.includes(obj.goodsId) && index < this.k) {
grade += obj.grade
}
}
return { goodsId, grade }
}

getInitialData() {

this.userPerferList = this.data.reduce((array, obj) => {
if (obj.userId === this.userId && !array.includes(obj.goodsId)) {
array.push(obj.goodsId)
}
return array
}, [])

this.goodsMayPerferList = this.data.reduce((array, obj) => {
if (!array.includes(obj.goodsId) && !this.userPerferList.includes(obj.goodsId)) {
array.push(obj.goodsId)
}
return array
}, [])
}

getGoodsGrade(isDelSelf, goodsId) {
this.simpleList = []
this.goodsId = goodsId

this.getGoodsList()

this.users = this.getGoodsUserNum(this.goodsId)

for (let goodsId of this.goodsList.values()) {
this.getGoodsSimple(goodsId)
}

this.simpleList.sort((a, b) => {

return b.grade - a.grade
})

if (isDelSelf) {
this.getNotSelfGoods()
}

this.gradeNormalization()
return this.simpleList
}


getGoodsList() {

const goodsArray = this.data.reduce((array, obj) => {
if (obj.goodsId !== this.goodsId) {
array.push(obj.goodsId)
}
return array
}, [])

const goods = [...new Set(goodsArray)]

this.goodsList = goods
}

getNotSelfGoods() {

const userGoods = this.data.reduce((array, obj) => {
if (obj.userId === this.userId) {
array.push(obj.goodsId)
}
return array
}, [])

for (let [index, obj] of this.simpleList.entries()) {
if (userGoods.includes(obj.goodsId)) {
this.simpleList.splice(index, 1)
}
}
}

getGoodsSimple(goodsId) {
const users = this.getGoodsUserNum(goodsId)

const bottom = Math.sqrt(this.users.length * users.length)
let count = 0

for (let val of users.values()) {
if (this.users.includes(val)) {

count += this.getSimpleElememt(val)
}
}

const res = {
goodsId,
grade: count / bottom
}
this.simpleList.push(res)
}

getSimpleElememt(userId) {

const goodsNum = this.data.reduce((array, obj) => {
if (obj.userId === userId) {
array.push(obj.goodsId)
}
return array
}, [])
const count = [...new Set(goodsNum)].length
const element = 1 / Math.log(1 + count)
return element
}

getGoodsUserNum(goodsId) {

const users = this.data.reduce((array, obj) => {
if (obj.goodsId === goodsId) {
array.push(obj.userId)
}
return array
}, [])
return [...new Set(users)];
}

gradeNormalization() {

const max = this.simpleList[0].grade
for (let index of this.simpleList.keys()) {
this.simpleList[index].grade = this.simpleList[index].grade / max
}
}
}

module.exports = { RecommendUserService, RecommendGoodsService }